package emby.lang;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import emby.err.InvalidArgumentError;
import emby.overload.OverloadCallable;
import emby.util.ReflectionHelp;

/**
 * A function object for Java methods. This is the solution to Java's
 * method overloading.
 * JavaMethodWrapper holds multiple methods with the same name because
 * the signature cannot be fully known until the function is actually called.
 * When this object is called upon, it selects the method with the matching
 * signature to the arguments and executes it.
 * <br><br>
 * Java Method Wrappers are attached to
 * a specific owner object that the method is called on.
 * <br>
 * Static methods have a null owner.
 * @author Justin
 */
public class JavaMethodWrapper extends EmbyObject implements OverloadCallable, Serializable{

	private static final long serialVersionUID = 737429061932098249L;
	Object owner;
	Method[] methods; //Array because multiple methods may have the same name
	String methodName;
	public JavaMethodWrapper( Object owner, Method[] methods){
		this.owner = owner;
		this.methods = methods;
		this.methodName = methods[0].getDeclaringClass().getCanonicalName()+"."+methods[0].getName();
	}

	/**
	 * Looks for the method with the correct signature and executes it with the
	 * given arguments on the OWNER object, which is specified during this
	 * wrapper's construction.
	 */
	public Object execute(int lineNumber, Scope s, Object... args) {
		Object o = EmbyNull.Null;
		//Look for the method with the right signature
		Method correctMethod = null;
		for( int i = 0; i<methods.length; i++){
			Class<?>[] parameterTypes = methods[i].getParameterTypes();
			if( ReflectionHelp.argsMatch(parameterTypes, args)){
				correctMethod = methods[i];
				break;
			}
		}

		if( correctMethod==null){
			throw new InvalidArgumentError(lineNumber, "Reflective java call on "+o.getClass()+" has invalid args. Also, emby cannot handle varargs.");
		}

		try {
			correctMethod.setAccessible(true);
			o=correctMethod.invoke(owner, args);
		} catch (IllegalArgumentException e) {
			throw new InternalError("Invalid args on JavaMethodWrapper reflection. (Should not happen: the for loop looks for a matching method)");
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.getTargetException().printStackTrace();
		}
		return o;
	}


	public String toString(){
		return "JavaMethodWrapper:"+methodName;
	}
}
